package com.nxn.core.dao.hibernate;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.util.Assert;

import com.nxn.core.dao.GenericDao;
import com.nxn.core.utils.Page;

public abstract class GenericDaoHibernate<T, PK extends Serializable> implements GenericDao<T, PK> {
    /**
     * Log variable for all child classes. Uses LogFactory.getLog(getClass()) from Commons Logging
     */
    protected final Log log = LogFactory.getLog(getClass());
    private HibernateTemplate hibernateTemplate;
    private SessionFactory sessionFactory;
    
    public HibernateTemplate getHibernateTemplate() {
        return this.hibernateTemplate;
    }

    public SessionFactory getSessionFactory() {
        return this.sessionFactory;
    }
    
    @Autowired
    @Required
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }
    
    /**
     * 初始化对象.
     * 使用load()方法得到的仅是对象Proxy, 在传到View层前需要进行初始化.
     * 如果传入entity, 则只初始化entity的直接属性,但不会初始化延迟加载的关联集合和属性.
     * 如需初始化关联属性,需执行:
     * Hibernate.initialize(user.getRoles())，初始化User的直接属性和关联集合.
     * Hibernate.initialize(user.getDescription())，初始化User的直接属性和延迟加载的Description属性.
     */
    public void initProxyObject(Object proxy) {
    	Hibernate.initialize(proxy);
    }
    
    /**
	 * 根据主键查找实体类,找不到返回null
	 * 
	 * @param id  实体类主键
	 * @return    实体类对象
	 * @throws DataAccessException
	 */
    @SuppressWarnings("unchecked")
    public T get(Class<T> entityClass, PK id) {
        T entity = (T) getHibernateTemplate().get(entityClass, id);

        if (entity == null) {
            log.warn("Uh oh, '" + entityClass + "' object with id '" + id + "' not found...");
            throw new ObjectRetrievalFailureException(entityClass, id);
        }

        return entity;
    }
    
    /**
     * 获得所有实体类对象
     * 
     * @param <T>  		       泛型参数
     * @param entityClass  实体类的类型
     * @return			        实体类对象列表
     * @throws DataAccessException
     */
    @SuppressWarnings("unchecked")
    public List<T> getAll(Class<T> entityClass) {
        return getHibernateTemplate().loadAll(entityClass);
    }

    /**
     * 根据主键查找实体类,判断是不是已经存在
     * 
     * @param entityClass 	实体类的类型
     * @param id			实体类主键
     * @return
     */
    @SuppressWarnings("unchecked")
    public boolean exists(Class<T> entityClass, PK id) {
        T entity = (T) getHibernateTemplate().get(entityClass, id);
        return entity != null;
    }
    
	/**
	 * 更新或创建实体对象
	 * 
	 * @param  entity  实体对象
	 * @throws DataAccessException
	 */
    @SuppressWarnings("unchecked")
    public T save(T entity) {
        return (T) getHibernateTemplate().merge(entity);
    }

	/**
	 * 删除实体对象
	 * 
	 * @param  entity  实体对象
	 * @throws DataAccessException
	 */
    public void remove(T entity) {
        hibernateTemplate.delete(entity);
    }
    
    /**
     * 根据具体的某一个属性查询实体对象
     * 
     * @param entityClass		实体类的类型
     * @param propertyName		属性名
     * @param value				属性值
     * @return
     */
    
    @SuppressWarnings("unchecked")
    public List<T> findByProperty(Class<T> entityClass, String propertyName, Object value) {
        String[] obj = entityClass.toString().split("\\.");
        String realObj = obj[obj.length - 1];
        try {
            String queryString = "from " + realObj + " as model where model."
                    + propertyName + "= ?";
            return hibernateTemplate.find(queryString, value);
        } catch (RuntimeException re) {
            throw re;
        }
    }
    
	/**
	 * 根据查询语句查询
	 * 
	 * @param <T>
	 *            实体类的类型
	 * @param hql
	 *            查询语句
	 * @param values
	 *            查询参数
	 * @return 查询结果集
	 * @throws DataAccessException
	 * 
	 */
    @SuppressWarnings("unchecked")
    public List<T> findByHql(String hql, Object... values) {
        return hibernateTemplate.find(hql, values);
    }
    
	/**
	 * 根据hibernate注解或映射文件的查询语句查询
	 * 
	 * @param <T>
	 *            实体类的类型
	 * @param queryName
	 *            查询语句的名称 (语句包含?占位符)
	 * @param values
	 *            参数名称 (?占位符的值)
	 * @return 结果集
	 * @throws DataAccessException
	 * 
	 */
    @SuppressWarnings("unchecked")
    public List<T> findByNamedQuery(String queryName, Object... values) {
        return hibernateTemplate.findByNamedQuery(queryName, values);
    }
    
    @SuppressWarnings("unchecked")
    public List<T> findByNamedQueryAndNameParam(String queryName, Map<String, Object> queryParams) {
        String[] params = new String[queryParams.size()];
        Object[] values = new Object[queryParams.size()];
        
        int index = 0;
        for (String s : queryParams.keySet()) {
            params[index] = s;
            values[index++] = queryParams.get(s);
        }

        return getHibernateTemplate().findByNamedQueryAndNamedParam(queryName, params, values);
    }
    
    //-- 分页查询函数  start --//
    
    /**
     * 按HQL分页查询.
     * 
     * @param page 分页参数. 注意不支持其中的orderBy参数.
     * @param hql HQL语句.
     * @param values 数量可变的查询参数,按顺序绑定.
     * 
     * @return 分页查询结果, 附带结果列表及所有查询输入参数.
     */
	@SuppressWarnings("unchecked")
	public Page<T> queryPage(final Page<T> page, final String hql, final Object... values) {

		if (page.isAutoCount()) {
			long totalCount = countHqlResult(hql, values);
			page.setTotalItems(totalCount);
		}

		List<T> result = (List<T>) this.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
					Query query = session.createQuery(hql);
					if (values != null) {
						for (int i = 0; i < values.length; i++) {
							query.setParameter(i, values[i]);
						}
					}

					setPageParameterToQuery(query, page);

					return query.list();
				}
			});

		page.setResult(result);
		return page;
	}
	
	/**
	 * 执行count查询获得本次HQL查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的HQL语句,复杂的HQL查询请另行编写count语句查询.
	 */
	@SuppressWarnings("unchecked")
	protected long countHqlResult(final String hql, final Object... values) {
		String countHql = prepareCountHql(hql);

		try {
			List<Long> countlist = this.getHibernateTemplate().find(countHql,values);
			long totalCount = countlist.get(0);
			
			return totalCount;
		} catch (Exception e) {
			throw new RuntimeException("hql can't be auto count, hql is:" + countHql, e);
		}
	}
	
	private String prepareCountHql(String orgHql) {
		String fromHql = orgHql;
		// select子句与order by子句会影响count查询,进行简单的排除.
		fromHql = "from " + StringUtils.substringAfter(fromHql, "from");
		fromHql = StringUtils.substringBefore(fromHql, "order by");

		String countHql = "select count(*) " + fromHql;
		return countHql;
	}

	/**
	 * 设置分页参数到Query对象,辅助函数.
	 */
	protected Query setPageParameterToQuery(final Query q, final Page<T> page) {
		Assert.isTrue(page.getPageSize() > 0, "Page Size must larger than zero");

		//hibernate的firstResult的序号从0开始
		q.setFirstResult(page.getOffset());
		q.setMaxResults(page.getPageSize());
		return q;
	}
	
    /**
     * 按HQL分页查询.
     * 
     * @param page 分页参数. 注意不支持其中的orderBy参数.
     * @param hql hql语句.
     * @param values 命名参数,按名称绑定.
     * 
     * @return 分页查询结果, 附带结果列表及所有查询输入参数.
     */
	@SuppressWarnings("unchecked")
	public Page<T> queryPage(final Page<T> page, final String hql, final Map<String, ?> values) {

		if (page.isAutoCount()) {
			long totalCount = countHqlResult(hql, values);
			page.setTotalItems(totalCount);
		}
		List<T> result = (List<T>) this.getHibernateTemplate().executeFind(new HibernateCallback() {
			@Override
			public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.createQuery(hql);
				if (values != null) {
					query.setProperties(values);
				}

				setPageParameterToQuery(query, page);

				return query.list();
			}
		});

		page.setResult(result);
		return page;
	}

    /**
     * 执行count查询获得本次Hql查询所能获得的对象总数.
     * 
     * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
     */
	@SuppressWarnings("unchecked")
	protected long countHqlResult(final String hql, final Map<String, ?> values) {
		String countHql = prepareCountHql(hql);

		try {
			List<Long> countlist = this.getHibernateTemplate().findByValueBean(countHql,values);
			return countlist.get(0);
		} catch (Exception e) {
			throw new RuntimeException("hql can't be auto count, hql is:" + countHql, e);
		}
	}
	
    //-- 分页查询函数  end --//
}
